package org.apache.velocity.runtime.parser.node;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import org.apache.commons.lang.StringUtils;
import org.apache.velocity.exception.VelocityException;
import org.apache.velocity.runtime.log.Log;
import org.apache.velocity.util.introspection.Introspector;

public class PropertyExecutor extends AbstractExecutor {
	
	private final Introspector introspector;

	private Field propertyField;

	@Override
	public boolean isAlive() {
		return this.propertyField!=null || this.getMethod()!=null;
	}

	/**
	 * @param log
	 * @param introspector
	 * @param clazz
	 * @param property
	 * @since 1.5
	 */
	public PropertyExecutor(final Log log, final Introspector introspector,
			final Class clazz, final String property) {
		this.log = log;
		this.introspector = introspector;

		if (StringUtils.isNotEmpty(property)) {
			discover(clazz, property);
		}
	}

	protected Introspector getIntrospector() {
		return this.introspector;
	}

	protected void discover(final Class clazz, final String property) {
		try {
			Object[] params = {};

			StringBuffer sb = new StringBuffer("get");
			sb.append(property);
			try{
				propertyField = clazz.getField(property);
			}catch(Exception e){
				//do nothing
			}
			
			if(!isAlive()){
				setMethod(introspector.getMethod(clazz, property, params));
			}
			
			if(!isAlive()){
				setMethod(introspector.getMethod(clazz, sb.toString(), params));
			}
			
			if (!isAlive()) {
				char c = sb.charAt(3);

				if (Character.isLowerCase(c)) {
					sb.setCharAt(3, Character.toUpperCase(c));
				} else {
					sb.setCharAt(3, Character.toLowerCase(c));
				}

				setMethod(introspector.getMethod(clazz, sb.toString(), params));
			}
		} catch (RuntimeException e) {
			throw e;
		} catch (Exception e) {
			String msg = "Exception while looking for property getter for '"
					+ property;
			log.error(msg, e);
			throw new VelocityException(msg, e);
		}
	}

	public Object execute(Object o) throws IllegalAccessException,
			InvocationTargetException {
		if(propertyField != null){
			try {
				return propertyField.get(o);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return isAlive() ? getMethod().invoke(o, ((Object[]) null)) : null;
	}
}